/***************************************************************************
 *   oboe.resources.Manager						*
 *   Copyright (C) 2011 by Felipe Manga					*
 *   lufeboma@gmail.com							*
 *									 *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the	   *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to	*
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:						 *
 *									 *
 *   The above copyright notice and this permission notice shall be	*
 *   included in all copies or substantial portions of the Software.	   *
 *									 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,	   *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF	*
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR	 *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.					   *
 ***************************************************************************/

package oboe.resources;

//	import banjo3D.parsers.Parser;

import com.adobe.serialization.json.JSON;

import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.media.Sound;
import flash.media.SoundLoaderContext;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.utils.setTimeout;

import oboe.core.Oboe;
import oboe.math.Mesh3D;
import oboe.resources.zip.ZipEntry;
import oboe.resources.zip.ZipFile;
import oboe.utils.MD2;
import oboe.utils.MD2Parser;
import oboe.utils.audiofx.mp3.MP3FileReferenceLoader;
import oboe.utils.audiofx.mp3.MP3SoundEvent;


/**
 * Singleton class. Loads resources (images/models/swfs/zips/etc) and calls their respective initialization routines based on file extension. 
 * @author FelipeManga
 * 
 */
import flash.Error;

class Manager
{
private static var _instance : Manager;
private var queue : Dictionary;
private var outData : Array<Dynamic>;
private var PROJECT_ROOT : String;

/**
 * 
 * @return a reference to the instance of this class. If there is none, one is created and stored. 
 * 
 */
public static var instance(getInstance, null):Manager;
 	private function getInstance() : Manager {
	if( !_instance ) _instance = new Manager();
	return _instance;
}

public function new()
{
	if( _instance ) throw new Error( "onrock.resources.Manager REINSTANCIATION");
	this.PROJECT_ROOT = Oboe.flags["RESOURCES"];
}

private static function getExt(str : String):String{
	return str.substr( str.lastIndexOf(".") + 1 ).toLowerCase();
}

private function decodeFile( data : Object, qe : QueueElement ) : Void {
	var out : Object;
	switch( qe.extension ){
	case "json":
	if( data is ByteArray ) data = data.toString();
	out = JSON.decode( data as String );
	break;
/* * / // Banjo3D
	case "pms":
	case "pmz":
	case "pmzl":
	case "md2":
	out = Parser.run( data as ByteArray, qe.extension );
	break;
/*/	// Oboe
	case "md2":
	qe.bytes = data as ByteArray;
	makeParser( qe );
	out = null;
	break;
/* */
	case "png":
	case "jpg":
	case "jpeg":
	case "swf":
	case "gif":
	case "mp3":
	makeLoader( qe );
	out = null;
	break;
	case "plist":
	if( data is ByteArray ) data = data.toString();
	out = PlistParser.run( String(data) );
	break;
	case "txt":
	if( data is ByteArray ) data = data.toString();
	out = data;
	break;
	case "xml":
	out = new XML( data );
	break;
	case "zip":
	var zip : ZipFile = new ZipFile( data );
	var d : Object = { filesLeft:zip.entries.length, queueElement:qe };
	for( var i:Int = 0; i<zip.entries.length; ++i ){
		var entry : ZipEntry = zip.entries[i];
		var nqe : QueueElement = new QueueElement( qe.url+"/"+entry.name, zipDone, d, getExt(entry.name) ); 
		instance.queue[nqe] = nqe;
		nqe.bytes = zip.getInput( entry );
		// decodeFile( nqe.bytes, nqe );
		if( Oboe.flags["SLOW_LOAD"] )
		{
		setTimeout( decodeFile, 10+Math.random()*500, nqe.bytes, nqe );
		}	
		else
		{
		decodeFile( nqe.bytes, nqe );
		}
		
		out = null;
	}
	break;
	default:
	out = data;
	break;
	}
	if( out != null ) fileReady( out, qe );
}

private function zipDone( obj : Object ) : Void {
	obj.extra.filesLeft--;
	Files.registerResource( obj.url, obj.object, obj.appDomain );
	if( obj.extra.filesLeft <= 0 ){
	fileReady( null, obj.extra.queueElement );
	}
}

private function fileReady( data:Object, qe : QueueElement ) : Void {
	if( qe.callback != null )
	{
	qe.callback( { object: data, url: qe.url, appDomain: qe.appDomain, extra:qe.extra } );
	}
}

private function downloadNext( e : Event, currentData : QueueElement ) : Void {
	var data : Object;
	if( e.target is LoaderInfo ){
	if( Oboe.flags["DEBUG"] ) trace( "[FILE] ", currentData.url, " - transfer complete" );
	fileReady( (e.target as LoaderInfo).content, currentData );
	}else if( e.target is Sound ){
	if( Oboe.flags["DEBUG"] ) trace( "[FILE] ", currentData.url, " - transfer complete" );
	fileReady( e.target, currentData );
	}else if( e is MP3SoundEvent ){
	if( Oboe.flags["DEBUG"] ) trace( "[FILE] ", currentData.url, " - transfer complete" );
	fileReady( MP3SoundEvent(e).sound, currentData );
	}else if( e.target is MD2Parser ){
	if( Oboe.flags["DEBUG"] ) trace( "[FILE] ", currentData.url, " - parsing complete" );
	fileReady( e.target.data, currentData );
	}else{
	if( Oboe.flags["DEBUG"] ) trace( "[FILE] ", currentData.url, " - transfer complete" );
	decodeFile( (e.target as URLLoader).data, currentData );
	}
	delete instance.queue[ currentData ];
}

private static function makeParser( qe : QueueElement ):Void
{
	var cb : Function =	function ( e : Event ) : Void{ 
	(e.target as EventDispatcher).removeEventListener(e.type, arguments.callee);
	_instance.downloadNext(e, nqe); 
	};
	var nqe:QueueElement = new QueueElement( qe.url, qe.callback, qe.extra, qe.extension );
	nqe.ldr = new MD2Parser( qe.bytes );
	nqe.ldr.addEventListener( Event.COMPLETE, cb );
	instance.queue[nqe] = nqe;
}

private static function makeLoader( qe : QueueElement ) : Void {
	var req : URLRequest;
	if( !qe.bytes ) req = new URLRequest( qe.url );
	var cb : Function =	function ( e : Event ) : Void{ 
	(e.target as EventDispatcher).removeEventListener(e.type, arguments.callee);
	_instance.downloadNext(e, qe); 
	};
	var swfdata : Dynamic = null;
	switch(qe.extension){
	case "swf": 
		qe.appDomain = new ApplicationDomain( /* ApplicationDomain.currentDomain */	 );
		swfdata = new LoaderContext( false, qe.appDomain );
	case "png":
	case "jpg":
	case "jpeg":
	case "bmp":
	case "gif":
		qe.ldr = new Loader();
		qe.ldr.contentLoaderInfo.addEventListener( Event.COMPLETE, cb );
		qe.ldr.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, function ( e : Event ) : Void {
		if( Oboe.flags["DEBUG"] ) trace( "Error loading: ", qe.url );
		});
		if( req )
		{
		(qe.ldr as Loader).load( req, swfdata );
		}else{
		(qe.ldr as Loader).loadBytes( qe.bytes, swfdata );
		} 
		
		break;
	case "mp3":
		var context:SoundLoaderContext = new SoundLoaderContext(8000, false);
		if( req )
		{
		qe.ldr = new Sound( req, context );
		Sound(qe.ldr).addEventListener( Event.COMPLETE, cb );
		qe.ldr.addEventListener( IOErrorEvent.IO_ERROR, function ( e : Event ) : Void {
			if( Oboe.flags["DEBUG"] ) trace( "Error loading: ", qe.url );
		});
		if( !Oboe.flags["WAIT_FOR_MP3"] )
		{
			(qe.ldr as Sound).dispatchEvent( new Event(Event.COMPLETE) );
		}
		}
		else
		{
		qe.ldr = new MP3FileReferenceLoader();
		MP3FileReferenceLoader(qe.ldr).getSoundFromByteArray( qe.bytes );
		qe.ldr.addEventListener( Event.COMPLETE, cb );
		qe.ldr.addEventListener( IOErrorEvent.IO_ERROR, function ( e : Event ) : Void {
			trace( "Error loading: ", qe.url );
		});
		}
		break;
	case "txt":
	case "json":
	case "xml":
	case "ini":
	case "plist":
		qe.ldr = new URLLoader();
		(qe.ldr as URLLoader).dataFormat = flash.net.URLLoaderDataFormat.TEXT;
		qe.ldr.addEventListener( IOErrorEvent.IO_ERROR, function ( e : Event ) : Void {
		if( Oboe.flags["DEBUG"] ) trace( "Error loading: ", qe.url );
		});
		qe.ldr.addEventListener( Event.COMPLETE, cb );
		qe.ldr.load( req );
		break;
	case "pms":
	case "pmz":
	case "pmzl":
	case "bin":
	case "md2":
	case "zip":
	default:
		qe.ldr = new URLLoader();
		qe.ldr.dataFormat = flash.net.URLLoaderDataFormat.BINARY;
		qe.ldr.addEventListener( Event.COMPLETE, cb );
		qe.ldr.addEventListener( IOErrorEvent.IO_ERROR, function ( e : Event ) : Void {
		if( Oboe.flags["DEBUG"] ) trace( "Error loading: ", qe.url );
		});
		qe.ldr.load( req );
		break;
	}
}

public static function getFromClass( c:Class, ext:String, callback : Function = null ):Void
{
	if( !instance.queue ) instance.queue = new Dictionary();
	var qe : QueueElement = new QueueElement(
	null,
	callback,
	null,
	ext
	);
	
	_instance.decodeFile( new c(), qe );
	
}

/**
 * Adds a file to the loading queue. 
 * @param link The address of the file to load. If this URL does not start with "http:", the file is loaded relative to the PROJECT_ROOT/resources/ folder.
 * @param callback The function to call when this file is done loading.
 * @param extraData generic reference to data that should be returned along with the file.
 * 
 */
public static function getURL( link : String, callback : Function = null, extraData : Dynamic = null ) : Void {
	var ext : String = getExt(link);
	if( !instance.queue ) instance.queue = new Dictionary();
	var fullurl : String = link;
	
	if( fullurl.substr(0, 6).toLowerCase() == "alias:" )
	{
	fullurl = fullurl.substr( fullurl.indexOf("@")+1 );
	}
	
	if( fullurl.substr(0, 4 ).toLowerCase() == "rel:" )
	{
	fullurl = fullurl.substr(4);
	}
	else if( fullurl.substr(0, 5).toLowerCase() != "http:" )
	{
	fullurl = instance.PROJECT_ROOT + fullurl; 
	}
	
	var qe : QueueElement = new QueueElement(
	fullurl,
	callback,
	extraData,
	ext
	);
	
	instance.queue[ qe ] = qe;
	
	makeLoader( qe );
}
}
}

import flash.media.Sound;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.utils.ByteArray;

class QueueElement {
public var handle: String;
public var url : String;
public var callback: Function;
public var ldr: Dynamic;
public var extra: Dynamic;
public var extension : String;
public var appDomain : ApplicationDomain;
public var bytes : ByteArray;
public function QueueElement( 
url:String, 
callback:Function,
extra: Dynamic, 
extension : String 
){
this.url = url;
this.callback = callback;
this.extra = extra;
this.extension = extension;
}